using System;
using System.Reflection;

namespace DotNetCRM.Core.ExtensionMethods
{
    /// <summary>
    /// Use ReflectionHelper for all your Reflection needs.
    /// </summary>
    public static class ReflectionExtensions
    {
        /// <summary>
        /// Invoke a method on an Object.
        /// </summary>
        /// <param name="methodName">The method to invoke.</param>
        /// <param name="obj">The object containing the method.</param>
        /// <param name="parameters">Any method parameters</param>
        /// <returns>The return value from the method.</returns>
        public static object InvokeMethod(this object obj, string methodName,
                                          params object[] parameters)
        {
            obj.CheckNull("obj");
            return ReflectionHelper.InternalInvokeMember(obj.GetType(), obj, methodName, 
                                                         BindingFlags.InvokeMethod, null, parameters);
        }

        /// <summary>
        /// Invoke a static method on an Object.
        /// </summary>
        /// <param name="methodName">The method to invoke.</param>
        /// <param name="type">The object type</param>
        /// <param name="parameters">An method parameters</param>
        /// <returns>The return value from the method.</returns>
        public static object InvokeStaticMethod(this Type type, string methodName,
                                                params object[] parameters)
        {
            return ReflectionHelper.InternalInvokeMember(type, null, methodName,
                                                         BindingFlags.InvokeMethod, null, parameters);
        }

        /// <summary>
        /// Invoke a method on an Object.
        /// </summary>
        /// <param name="methodName">The method to invoke.</param>
        /// <param name="obj">The object containing the method.</param>
        /// <param name="genericParameters">Generic parameter types.</param>
        /// <param name="parameters">Any method parameters</param>
        /// <returns>The return value from the method.</returns>
        public static object InvokeMethod(this object obj, string methodName, Type[] genericParameters,
                                          params object[] parameters)
        {
            obj.CheckNull("obj");
            return ReflectionHelper.InternalInvokeMember(obj.GetType(), obj, methodName,
                                                         BindingFlags.InvokeMethod, genericParameters, parameters);
        }

        /// <summary>
        /// Invoke a static method on an Object.
        /// </summary>
        /// <param name="methodName">The method to invoke.</param>
        /// <param name="type">The object type</param>
        /// <param name="genericParameters">genericParameters</param>
        /// <param name="parameters">Any method parameters</param>
        /// <returns>The return value from the method.</returns>
        public static object InvokeStaticMethod(this Type type, string methodName, Type[] genericParameters,
                                                params object[] parameters)
        {
            return ReflectionHelper.InternalInvokeMember(type, null, methodName,
                                                         BindingFlags.InvokeMethod, genericParameters, parameters);
        }

        /// <summary>
        /// Get the specified property from the Object.
        /// </summary>
        /// <param name="propertyName">The property to get.</param>
        /// <param name="obj">The object containing the property.</param>
        /// <returns>The property value.</returns>
        public static object GetProperty(this object obj, string propertyName)
        {
            obj.CheckNull("obj");
            return ReflectionRegistry.GetPropertyAccessor(obj.GetType(), propertyName).Invoke(obj);
            //return ReflectionHelper.InternalInvokeMember(obj.GetType(), obj, propertyName,
            //    BindingFlags.GetProperty, null);
        }

        /// <summary>
        /// Get the specified static property from the type.
        /// </summary>
        /// <param name="propertyName">The property to get.</param>
        /// <param name="type">The type of the class.</param>
        /// <returns>The property value.</returns>
        public static object GetStaticProperty(this Type type, string propertyName)
        {
            return ReflectionHelper.InternalInvokeMember(type, null, propertyName, 
                                                         BindingFlags.GetProperty, null);
        }

        /// <summary>
        /// Set the specified property on the Object.
        /// </summary>
        /// <param name="propertyName">The property to set.</param>
        /// <param name="obj">The object containing the property.</param>
        /// <param name="parameters">The data used in setting the property.</param>
        public static void SetProperty(this object obj, string propertyName,
                                       params object[] parameters)
        {
            obj.CheckNull("obj");
            ReflectionHelper.InternalInvokeMember(obj.GetType(), obj, propertyName,
                                                  BindingFlags.SetProperty, null, parameters);
        }

        /// <summary>
        /// Set the specified property on the Object.
        /// </summary>
        /// <param name="propertyName">The property to set.</param>
        /// <param name="type">The type of the class.</param>
        /// <param name="parameters">The data used in setting the property.</param>
        public static void SetStaticProperty(this Type type, string propertyName,
                                             params object[] parameters)
        {
            ReflectionHelper.InternalInvokeMember(type, null, propertyName,
                                                  BindingFlags.SetProperty, null, parameters);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="fieldname"></param>
        /// <param name="value"></param>
        public static void SetField(this object obj, string fieldname, object value)
        {
            obj.CheckNull("obj");
            ReflectionHelper.InternalSetField(obj.GetType(), obj, fieldname, value);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="fieldname"></param>
        /// <returns></returns>
        public static object GetField(this object obj, string fieldname)
        {
            obj.CheckNull("obj");
            return ReflectionRegistry.GetFieldAccessor(obj.GetType(), fieldname).Invoke(obj);
            //return ReflectionHelper.InternalInvokeMember(obj.GetType(), obj, fieldname, BindingFlags.GetField, null);
        }
    }
}